# # -*- coding: utf-8 -*-
# # @Time    : 2025/5/19 8:04
# # @FileName: yk_model.py
# # @Software: PyCharm
# 07 - 0
# 8 - 11 - 01 - 01 - cifar10 - LeNet - 5
# 物体识别及RNN股票预测
# 1.
# 考试要求
#     按要求完成下面的各项需求。
#     必须有录屏，无录屏者一律0分处理，必须是完整的考试录屏，录屏过程中不允许有暂停行为，若是发现，按考试作弊处理，桌面必须有自己的学院、班级、姓名。
#     上交U盘时，U盘中只允许有自己考试的项目。
#     本项目的所有py代码上传到自己建立的数据仓库，并将上传成功的截图提交到考试网盘中
# 2.
# 项目简介
# 项目1：cifar10 - LeNet - 5
# 物体识别
# 一、项目背景
# 本项目旨在构建一个基于深度学习的图像识别系统，该系统能够识别并分类上传的图像。系统后端采用Flask框架搭建，前端使用HTML构建简单的用户界面。图像识别模型采用LeNet - 5
# 卷积神经网络，并通过TensorFlow和Keras框架实现。
# 二、功能需求
# 图像预处理：系统应能够接收用户上传的图像，并对其进行必要的预处理，包括调整图像大小、归一化像素值等，以适应模型的输入要求。
# 图像识别：利用训练好的LeNet - 5
# 模型对用户上传的图像进行分类识别，输出识别结果。
# 结果展示：系统应能够将识别结果以友好的方式展示给用户，包括显示识别的图片类别和上传的图像。
# 模型加载与保存：系统应能够加载预训练的模型权重，并在需要时保存模型权重。
# 三、非功能需求
# 性能：系统应能够快速响应用户的请求，并在合理的时间内完成图像识别任务。
# 可用性：系统应具有简洁明了的用户界面，方便用户使用。
# 可扩展性：系统应设计得易于扩展，以便在未来添加更多功能或改进模型。
# 四、技术选型
# 后端框架：采用Flask作为后端框架，负责处理用户请求、调用模型进行图像识别并返回结果。
# 深度学习框架：使用TensorFlow和Keras构建和训练LeNet - 5
# 模型，用于图像识别任务。
# 前端技术：使用HTML构建用户界面，实现图像的上传和识别结果的展示。
# 五、数据需求
# 训练数据：系统需要使用CIFAR - 10
# 数据集进行模型的训练，该数据集包含10个类别的60000张32x32彩色图像。
# 测试数据：使用CIFAR - 10
# 数据集中的测试集对模型进行测试，以评估模型的性能。
# 用户上传数据：系统应能够处理用户上传的图像，并进行识别和分类。
# 六、实现方案
# 1.
# 模型约束描述
# 1)    数据加载与预处理
# 加载CIFAR - 10
# 数据集，该数据集包含50000张训练图像和10000张测试图像，每张图像的大小为32x32像素，共有3个颜色通道（RGB）。
# 对加载的图像数据进行归一化处理，即将像素值从[0, 255]
# 缩放到[0, 1]
# 范围内，以便于神经网络的处理。
import matplotlib.pyplot as plt
import numpy as np
from keras.src.datasets.cifar10 import load_data
from keras.src.legacy.preprocessing.image import ImageDataGenerator
from keras.src.utils import to_categorical
from keras import Model, Sequential, layers, losses, activations, metrics,optimizers

(x_train, y_train), (x_test, y_test) = load_data()
dim = len(np.unique(y_train))
x_train = x_train / 255
x_test = x_test / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
idg = ImageDataGenerator(rotation_range=30, zoom_range=0.2, shear_range=0.2)


# 2)    模型定义
# 定义了一个名为Lenet5的类，该类继承自tensorflow.keras.Model，表示一个基于LeNet - 5
# 架构的卷积神经网络模型。
class Lenet5(Model):
    # LeNet - 5
    # 模型包括两个卷积层（每个卷积层后跟一个最大池化层），以及三个全连接层。
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.conv = Sequential([
            # 卷积层C1：6 个5x5的卷积核，relu激活。
            layers.Conv2D(filters=6, kernel_size=5, activation=activations.relu),
            # 池化层S2：最大池化，池化窗口为2x2。
            layers.MaxPooling2D(2),
            # 卷积层C3：16 个5x5的卷积核，relu激活。
            layers.Conv2D(filters=16, kernel_size=5, activation=activations.relu),
            # 池化层S4：最大池化，池化窗口为2x2。
            layers.MaxPooling2D(2),
        ])

        self.fl = layers.Flatten()

        self.fc = Sequential([
            # 全连接层F5：120 个神经元，relu激活。
            layers.Dense(units=120, activation=activations.relu),
            layers.Dropout(0.2),
            # 全连接层F6：84个神经元，relu激活。
            layers.Dense(units=84, activation=activations.relu),
            layers.Dropout(0.2),
            # 输出层：10 个神经元，对应10个类别的概率分布。
            # 模型的输出层使用softmax激活函数，用于多分类问题。
            layers.Dense(units=dim, activation=activations.softmax)
        ])

    def call(self, x,*args, **kwargs):
        x = self.conv(x)
        x = self.fl(x)
        x = self.fc(x)
        return x

if __name__ == '__main__':
    # 3)    模型编译
    # 实例化Lenet5类，创建一个模型对象。
    model = Lenet5()
    # 模型创建并指定模型的输入形状。
    model.build(x_train.shape)
    model.summary()
    # 编译模型，指定合适的优化器，损失函数为稀疏分类交叉熵，并监控准确率指标。
    model.compile(
        optimizer=optimizers.Adam(),
        loss=losses.categorical_crossentropy,
        metrics=['acc',metrics.Precision,metrics.Recall,metrics.F1Score]
    )
    # 4)    模型训练
    # 训练模型，指定训练数据为(x_train, y_train)，验证数据为(x_test, y_test)。
    # 设置训练轮次为10，批次大小为100。
    # 训练过程中，模型的准确率和损失值将被记录并保存在log对象中。
    log = model.fit(idg.flow(
        x_train,y_train,batch_size=100),epochs=10,validation_data=(x_test,y_test)
    )
    # 5)    可视化训练过程
    # 提取训练和验证过程中的准确率数据。
    # 使用Matplotlib库绘制训练和验证准确率的折线图，以便直观地比较两者的变化趋势。
    acc = log.history['acc']
    val_acc = log.history['val_acc']
    plt.plot(acc,c='r',label='acc')
    plt.plot(val_acc,c='g',label='val_acc')
    plt.legend()
    plt.show()

    loss = log.history['loss']
    val_loss = log.history['val_loss']
    plt.plot(loss, c='b', label='loss')
    plt.plot(val_loss, c='y', label='val_loss')
    plt.legend()
    plt.show()
    # 6)    模型保存
    # 训练完成后，将模型的权重保存为HDF5文件（model.h5）。这样可以在以后重新加载模型时恢复其训练状态。
    model.save_weights('yk_model.weights.h5')
    model.load_weights('yk_model.weights.h5')
    model.evaluate(x_test,y_test)
    index = np.random.randint(0,len(x_test)-1)
    img = x_test[index]
    plt.imshow(img)
    plt.show()

